package com.bolinfest.closure;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.common.collect.ImmutableList;
import com.google.javascript.jscomp.CompilationLevel;
import com.google.javascript.jscomp.Compiler;
import com.google.javascript.jscomp.CompilerOptions;
import com.google.javascript.jscomp.JSSourceFile;

/** An example of how to call the Closure Compiler programmatically. */
public final class CallCompilerProgrammaticallyExample {

  /** This is a utility class that is not meant to be instantiated. */
  private CallCompilerProgrammaticallyExample() {};

  /**
   * Compiles the code in Simple mode with pretty print disabled by default.
   * @param code JavaScript source code to compile.
   * @return The compiled version of the code.
   */
  public static String compile(String code) {
    final boolean prettyPrint = false;
    return compile(code, CompilationLevel.SIMPLE_OPTIMIZATIONS,
        prettyPrint, ImmutableList.<JSSourceFile> of());
  }

  /**
   * Compiles the code using the specified configuration.
   * @param code JavaScript source code to compile.
   * @param level The compilation level to use when compiling the code.
   * @param boolean prettyPrint True to pretty-print the compiled code.
   * @param externs to use when compiling the code.
   * @return The compiled version of the code.
   */
  public static String compile(String code, CompilationLevel level,
      boolean prettyPrint, List<JSSourceFile> externs) {
    // A new Compiler object should be created for each compilation because
    // using a previous Compiler will retain state from a previous compilation.
    Compiler compiler = new Compiler();
 
    CompilerOptions options = new CompilerOptions();
    
    // setOptionsForCompilationLevel configures the appropriate options on the
    // CompilerOptions object.
    level.setOptionsForCompilationLevel(options);
    
    // Options can also be configured by modifying the Options object directly.
    options.prettyPrint = prettyPrint;

    // Input to the compiler must be associated with a JSSourceFile object.
    // The dummy input name "input.js" is used here so that any warnings or
    // errors will cite line numbers in terms of input.js.
    JSSourceFile input = JSSourceFile.fromCode("input.js", code);
    JSSourceFile[] inputArray = new JSSourceFile[] { input };

    // Convert the List to an array to match the Compiler's specification.
    JSSourceFile[] externsArray = externs.toArray(new JSSourceFile[0]);

    // compile() returns a Result, but it is not needed here.
    compiler.compile(externsArray, inputArray, options);

    // The Compiler is responsible for generating the compiled code; the
    // compiled code is not accessible via the Result.
    return compiler.toSource();
  }

  /** Runs compile() using some dummy inputs and prints the result. */
  public static void main(String[] args) {
    // Turn off the logging so it does not clutter up the output.
    Logger.getLogger("com.google.javascript.jscomp").setLevel(Level.OFF);
    
    // To get the complete set of externs, the logic in
    // CompilerRunner.getDefaultExterns() should be used here.
    JSSourceFile extern = JSSourceFile.fromCode("externs.js",
        "function alert(x) {}");
    List<JSSourceFile> externs = ImmutableList.of(extern);

    // Run the Compiler.
    String compiledCode = compile(
          "var hello = function(name) {"
        + "  alert('Hello, ' + name);"
        + "};"
        + "hello('New user');",
        CompilationLevel.ADVANCED_OPTIMIZATIONS,
        false, // do not prettyPrint the output
        externs);

    // Print the result, which is: alert("Hello, New user");
    System.out.println(compiledCode);
  }
}